`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: Rohit Kulkarni
// 
// Create Date: 01/14/2015 01:34:44 PM
// Design Name: 
// Module Name: HW_PWD
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module HW_PWD(
    input clk_HW_PWD,           //input clock to sample
    input pwm_in,               //PWM output from AXI timer
    input reset,

    output reg update_count,        //interrupt to Microblaze to update count
    output reg [31:0] low_timer,    //low_timer count
    output reg [31:0] high_timer    //high_timer count
);
   
   //Parameter for states
   parameter RESET      =   5'b00001; 
   parameter INITIALIZE =   5'b00010;
   parameter HIGH_COUNT =   5'b00100;
   parameter LOW_COUNT  =   5'b01000;
   parameter INTERRUPT  =   5'b10000;
   
 
    reg pwm_in_p;       //pulse width modulated signal PREVIOUS value 
    reg pwm_in_c;       //pulse width modulated signal CURRENT value
    reg zero_one_tran = 0;
    reg one_zero_tran = 0;
    reg [4:0] Present_State;
    reg [4:0] Next_State;
    reg [31:0] high_timer_local = 0;
    reg [31:0] low_timer_local = 0;
    reg high_timer_en = 0;			
    reg low_timer_en = 0;
    reg high_timer_rst = 0;	
    reg low_timer_rst = 0;	
    
    //Update the variable to the new PWM count signal  
    always @(posedge clk_HW_PWD)
    begin
        pwm_in_p    <=  pwm_in_c;
        pwm_in_c    <=  pwm_in;  
    end
    
    //Detect 0 -> 1 and 1 -> 0 transition here
    always @(posedge clk_HW_PWD)
    begin
        if(pwm_in_p != pwm_in_c)
        begin
            if(pwm_in_p == 1'b0 && pwm_in_c == 1'b1)
            begin
                zero_one_tran <= 1'b1;
                one_zero_tran <= 1'b0;
            end
            else
            begin
                one_zero_tran <= 1'b1;
                zero_one_tran <= 1'b0;
            end                
        end
        else
        begin
            one_zero_tran <= 1'b0;
            zero_one_tran <= 1'b0;                       
        end
    end
    
    
    //State machine for pulse width detection
    
    //Update State 
    always @(posedge clk_HW_PWD)
    begin
        if(reset)
            Present_State   <=  RESET;
        else
            Present_State   <=  Next_State;                                   
    end
    
    //Output State logic
    always @(*)
    begin
        case(Present_State)
            RESET:      begin
                            update_count        = 1'b0;                            
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en	    = 1'b0;	
                            low_timer_en	    = 1'b0;
                            high_timer_rst	    = 1'b1;
                            low_timer_rst	    = 1'b1;
                        end
                      
            INITIALIZE: begin
                            update_count        = 1'b0;                                                                                      
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
            			    high_timer_en	= 1'b0;	
                            low_timer_en	= 1'b0;
                            high_timer_rst	= 1'b1;
                            low_timer_rst	= 1'b1;	
                        end  
            
            HIGH_COUNT: begin
                            update_count        = 1'b0;                                                                                                                  
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                                              
                            high_timer_en	= 1'b1;	
                            low_timer_en	= 1'b0;	
                            high_timer_rst	= 1'b0;
                            low_timer_rst	= 1'b1;
                        end
                  
            LOW_COUNT:  begin
                            update_count        = 1'b0;                                                                                                                              
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en	= 1'b0;	
                            low_timer_en	= 1'b1;	
                            high_timer_rst	= 1'b0;
                            low_timer_rst	= 1'b0;
                        end
              
            INTERRUPT:  begin
                            update_count        = 1'b0;                
                            low_timer           = low_timer_local;
                            high_timer          = high_timer_local;                                                                                                                                                                  
                            high_timer_en	= 1'b0;	
                            low_timer_en	= 1'b0;
                            high_timer_rst	= 1'b0;
                            low_timer_rst	= 1'b0;	
                        end
                                 
            default:    begin
                            update_count        = 1'b0;                                                                                                                                                                  
                            low_timer           = low_timer;
                            high_timer          = high_timer;                                                                                                                                                                  
                            high_timer_en	= 1'b0;	
                            low_timer_en	= 1'b0;	
                            high_timer_rst	= 1'b1;
                            low_timer_rst	= 1'b1;	
                        end
        endcase               
    end
    
    //Next state logic
    always @(*)
    begin
        case (Present_State)
        RESET:      begin
                        if(one_zero_tran)
                    	   Next_State  =  INITIALIZE;
                    	else
                    	   Next_State  =  RESET;                                                                
                    end
          
        INITIALIZE: begin
                        if(zero_one_tran)
                            Next_State  =  HIGH_COUNT;
                         else
                            Next_State  =  INITIALIZE;                                                                                       
                    end  
        
        HIGH_COUNT: begin
                        if(one_zero_tran)
                            Next_State  =  LOW_COUNT;
                        else
                            Next_State  =  HIGH_COUNT;    
                    end 
              
        LOW_COUNT:  begin
                        if(zero_one_tran)
                            Next_State  =  INTERRUPT;
                        else
                            Next_State  =  LOW_COUNT;                                            
                    end
          
        INTERRUPT:  begin           //Interrupt needs to be active for 1 clock cycle since operating at 100MHz
                        Next_State  =  RESET;
                    end
                        
        default:    begin
                        Next_State  =  INITIALIZE;
                    end     
       endcase
    end  

	//Manage high timer here
	always @(posedge clk_HW_PWD)
	begin
	    if(high_timer_en)
	    begin
	        high_timer_local 	<= high_timer_local + 1'b1;
	    end		
	    else if(high_timer_rst)
		high_timer_local 	<= 32'b0;	
	    else
	        high_timer_local 	<= high_timer_local;
    end	            
   
    
	//Manage low timer here
	always @(posedge clk_HW_PWD)
	begin
	    if(low_timer_en)
	    begin
	        low_timer_local 	<= low_timer_local + 1'b1;
	    end		
	    else if(low_timer_rst)
		low_timer_local 	<= 32'b0;	
	    else
	        low_timer_local 	<= low_timer_local;
    end	            


endmodule